1. Field of the Invention
The invention relates to a method, system and program product for debugging and/or monitoring a computer instruction sequence. The term xe2x80x98program productxe2x80x99 here means a body of computer code stored by a machine readable storage medium such as a CD Rom or one or more floppy disks, or made available for downloading from a remote computer site. The computer code may form part of a computer program compiler, or it may be implemented as a debugger which stands alone or which is integrated into or provided as an add-on with another program, for example an editor/assembler or an operating system, or it may form part of one of various instrumentation tools for monitoring or analysing instruction sequences.
A computer or computer based apparatus, eg an industrial automation system, comprises a central processor unit (CPU), often a microprocessor, and random access memory for holding data and instructions for controlling the CPU. A debugging program may be used to cause another program called the xe2x80x9cdebuggeexe2x80x9d, generally a relatively low level computer program, eg at the CPU kernel code level, to run on the CPU whilst monitoring the running of the debuggee program. One function of the debugger might be to cause the debuggee program to execute one step at a time (called xe2x80x9csingle-steppingxe2x80x9d) or to permit the debuggee program to run continuously until it reaches one or each of more than one breakpoint installed in the debuggee program by the debugger. At the or each breakpoint, or after each single step, the debugger may cause the display of values of parameters such as the contents of particular CPU registers, in order to help the user trace errors (xe2x80x9cbugsxe2x80x9d) in the debuggee program. The means by which the debugger deals with such breakpoints is called herein the xe2x80x9cbreakpoint handling mechanismxe2x80x9d.
Note that it is possible to include breakpoints in the actual debuggee program but those breakpoints would need to be removed or made non-functional once the program has been debugged and the program is to be run normally. This invention is concerned with breakpoint handling by the debugger (or other tool or program incorporating a debugger).
2. Related Art
Thus, the basic functionality of breakpointing mechanisms in debuggers or various instrumentation tools is that of causing the generation of notifications/interceptions at desired points in an executing control flow sequence, where the points of interception are specified dynamically at run-time and not pre-programmed.
To assist breakpoint handling, many modern CPU architectures comprise breakpoint registers which can be used to make the processor generate an exception when the address programmed into one of the breakpoint registers is accessed, either for instruction execution or data access depending on the programmed settings. This facility is sometimes used to set breakpoints in code without having to patch the instruction stream explicitly with a breakpoint instruction. In multitasking systems, the operating system usually has support for saving/restoring these registers in the time of a context switch. However, since the number of such registers is usually very limited (eg four in the Intel Pentium platform), these are not sufficient for generic usage where the number of breakpoints required exceeds the number of registers available.
A typical approach, therefore, is to fall back on putting in breakpoint instruction patches once all the breakpoint registers have been used up for a process. In this case, the debugger replaces the instruction in the debuggee""s code stream where the breakpoint is desired with a breakpoint instruction. This will cause a trap whenever that instruction is executed. To continue program execution after breakpoint evaluation and processing is done each time the breakpoint is reached, the debugger puts back the original instruction at that point, makes the debuggee single-step this single instruction, and then replaces the breakpoint instruction (so that it is sure to be hit the next time the same code executes) before letting the debuggee continue at full speed.
The above approach which is used in many debuggers today relies on single-stepping to continue execution past a breakpoint. One disadvantage of this approach is that single-stepping can be a little expensive in terms of increasing the debugged program""s execution time. That is, the speed at which execution continues past an instruction where a breakpoint has been set. This is because when single step mode is turned on, there is an additional trap and hence switch from the debuggee to debugger on completion of the instruction, where the debuggee puts the breakpoint and then transfers back to the debugger, which would have been stopped till this gets done.
Another related problem is that the method leaves open a window (albeit small) of potential breakpoint misses in the case of multi-threaded debuggee""s, especially on multiprocessor systems. This requires some mechanism for stopping all other threads/processors.
Sometimes instruction emulation is used to avoid the need for single-stepping where possible, ie the debugger emulates the instruction instead of running it as a single step on the processor itself. However, this can be done only for a few instructions and has the disadvantage of dependence on knowledge of the instruction set of the processor. Another approach, used in some code patching dynamic instrumentation tools or dynamic debug APIs, is to actually relocate the original instruction to a different location in the debuggee""s address space and execute it from there, so that it is not necessary to put the original instruction back in its proper place. This avoids some of the problems cited earlier with in-place execution. However, transparent relocation is not very easy to achieve in all cases, is again dependent on knowledge of the instruction set of the processor, and can have unexpected side-effects in case of dependencies on the actual instruction address values that are hidden with the code/system logic.
In general terms the invention comprises a method, system and program product for operating a computer processor, which processor is coupled to memory means and which comprises breakpoint register means implemented as hardware in the processor. The invention comprises:
(i) storing, at respective addresses of said memory means, a sequence of processor instructions to be processed by the processor;
(ii) replacing one of said instructions in said sequence with a break instruction;
(iii) supplying said sequence of instructions including the break instruction in place of the said one instruction to said processor;
(iv) when the break instruction has been acted upon by the processor, entering the address of the break instruction in said breakpoint register means;
(v) replacing the said one instruction at its original address; and
(vi) causing the processor to resume on-going processing of the remainder of the sequence of instructions from and including said one instruction.
Thus, the breakpoint register means is used for a purpose that is different to the prior art. The restoration of the breakpoint instruction after execution past a breakpoint (after putting back the original instruction) is not done immediately. This enables the omission of the single-step after restoring the original instruction in the sequence described earlier. Instead, at that time, a breakpoint register is used to set an instruction breakpoint at that address, and then a flag (eg the RF flag in Intel) is set in the processor to ensure that the original instruction can execute without faulting right away, and yet cause a fault the next time the same point is hit. The next time the debugger gets control (say, when another breakpoint is hit) in the same process context, the breakpoint instruction can be put back (herein this is called xe2x80x9chardeningxe2x80x9d of the breakpoint), so that the breakpoint register is free for the next use.
Summarising the prior art method, to continue after processing a breakpoint, the following sequence of actions are carried out:
1. Stop other threads (or stop other processors and disable interrupts, if this is a kernel debugger)
2. Put back original instruction
3. Set the debuggee""s program counter to the address of the original instruction
4. Single-step the debuggee (give back control to the debugger after the original instruction is executed)
On completion of single-step (notification via an exception):
5. Turn off single-step
6. Set the breakpoint back again
7. Resume other threads (or resume other processors and enable interrupts back again in the case of kernel debuggers)
8. Continue debuggee
By comparison, the method to be described in the following detailed description comprises the following steps:
1. Harden previous breakpoint if any (if it is different from the current one) [xe2x80x9cHardeningxe2x80x9d involves putting back the breakpoint instruction at the previous breakpoint location]
2. Stop other threads
3. Set up an instruction breakpoint register for the current breakpoint (across processors on a symmetric multi-processor system (SMP) if there were threads in this process running on the other processorsxe2x80x94this might involve sending an IPI in the case of kernel debuggers).
4. Resume other threads
5. Put back the original instruction
6. Set the debuggee""s PC to the address of the original instruction
7. Set a special processor flag (RF or Resume flag in Intel) in the debuggee""s context for suppressing the breakpoint exception for just the next executing instruction [This needs to be done so that the original instruction can get executed as intended without faulting right-away]
8. Continue debuggee
Note: If the same point is hit again, then just steps 7 and 8 are enough (assuring that as on Intel, the PC is already set to the faulting instruction""s address which would be the same as the original instruction; if not then step 6 might be needed too).
Even a single breakpoint register is sufficient for this optimization to be possible. All that is happening is the delay of setting back the breakpoint instruction as far as possible, thus simplifying the control flow involved in getting the debuggee to run past the breakpoint.
As long as there is an operating system support for making the breakpoint register settings effective across all processors that the concerned process is executing on, this also avoids the window of potential breakpoint misses described earlier. Since internally this still involves stopping other threads while these settings are being changed, this is perhaps not a significant benefit in itself except in cases where the instruction being single-stepped is one that takes a long time to complete (eg if it causes a page fault).
Note that breakpoint execution may be faster as a result of avoiding the single-step. [The gain in speed may be more perceptible in conditional breakpoint evaluation situations, especially if the point happens to be in a loop, even more so in non-interactive debugging/instrumental tools]. The optimisation benefits the most frequently hit breakpoints (ie when the same breakpoint is hit repeatedly in succession without other intervening breakpoints) by its very nature, rather than having the user or even the debugger program trying to decide which breakpoints to set via hardware breakpoint registers to improve performance.
The performance benefits of making more debug registers available would be distributed more effectively across all breakpoints if the above steps are extended to use up all available breakpoint registers before attempt to reuse the register.
Not having to single-step may also simplify the management of breakpoint state information in some cases. For example, with the single-step based approach, global state may be required for tracking the currently executing breakpoint across the execution of the original instruction, especially in the case of kernel debuggers, where interrupts may have to be disabled right through the single-step to avoid nested execution complexities. In embodiments of the invention described herein, the main global state to be maintained is for remembering the last non-hardened breakpoint register at any point, while the breakpoint is hardened only before the next time there is a need for the breakpoint register. Problems may arise with kernel breakpoints in pageable kernel code if the next breakpoint occurs in an interrupt handler or some place where page faults cannot be taken. To overcome this the hardening of the earlier breakpoint could be safely delayed a little, say, if it is possible to come in the way of the page fault handling code to make sure that the breakpoint is hardened before the page can actually be accessed next.
Note that the method described herein relies on architectures that have debug/breakpoint registers. In addition, for an architecture where the breakpoint fault happens before the execution of the specified instruction, there should be some hardware support for a mechanism to delay the effect of the breakpoint register settings to immediately after the instruction that caused that just trapped (ie a provision for explicitly setting something like an RF flag in Intel or the PSW Z bit in HP PA-Risc).